package com.softserveinc.moviedbapp.fragment;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.ActionBar;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.ListFragment;
import android.support.v4.app.SupportActivity;
import android.util.Log;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;

import com.softserveinc.moviedbapp.R;
import com.softserveinc.moviedbapp.adapter.MovieAdapter;
import com.softserveinc.moviedbapp.application.MovieDBApplication;
import com.softserveinc.moviedbapp.manager.DataManager;
import com.softserveinc.moviedbapp.model.Movie;
import com.softserveinc.moviedbapp.provider.MovieListType;

import java.util.ArrayList;
import java.util.List;

public class MovieListFragment extends ListFragment implements ListView.OnScrollListener {

	private static final boolean DEBUG = true;
	private static final int DEFAULT_VISIBLE_ROWS = 4;

	private MovieListType fragmentType;
	private MovieAdapter movieAdapter;
	private ActionBar actiontionBar;
	private View progressBar;
	private String searchQuery;

	private boolean adapterEmpty = true;
	private boolean startupList;
	private boolean wentFetching;
	private boolean listReady;

	private int adapterChildCount;

	private OnMovieSelectedListener onMovieSelectedListener;

	private DataManager dataMananger;
	private ListView listView;

	private String TAG;

	public MovieListFragment(final MovieListType type) {
		fragmentType = type;
		TAG = MovieListFragment.class.getSimpleName() + " " + fragmentType;
		if (DEBUG) {
			Log.i(TAG, "constructor MovieListFragment()");
		}
	}

	/* a callback interface that notifies movie info fragment about the event in this fragment */
	public interface OnMovieSelectedListener {
		public void onMovieSelected(Movie movie);
	}

	@Override
	public void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (DEBUG) {
			Log.d(TAG, "onCreate()");
		}
		if (searchQuery == null) {
			setRetainInstance(true);
		}
	}

	@Override
	public void onAttach(final SupportActivity activity) {
		super.onAttach(activity);
		/* we want this page to display when orientation changes */
		onMovieSelectedListener = (OnMovieSelectedListener) activity;
		if (DEBUG) {
			Log.d(TAG, "onAttach()");
			Log.d(TAG, "state: adapterEmpty = " + adapterEmpty);
			Log.d(TAG, "state: fragmentType = " + fragmentType);
			Log.d(TAG, "state: dataMananger = " + dataMananger);
			Log.d(TAG, "state: adapterChildCount = " + adapterChildCount);
		}
	}

	@Override
	public void onActivityCreated(final Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		if (DEBUG) {
			Log.d(TAG, "inside onActivityCreated()");
		}
		if (savedInstanceState != null && !savedInstanceState.isEmpty()) {
			searchQuery = savedInstanceState.getString("search_query");
			Log.i(TAG, "onActivityCreated()  search query " + searchQuery);
		}
		/* get the reference to the action bar from the main fragment */
		actiontionBar = ((FragmentActivity) getActivity()).getSupportActionBar();
		progressBar = actiontionBar.getCustomView().findViewById(R.id.progressBar);
		listView = getListView();

		/* add a loading progress bar as a footer to the list view */
		if (fragmentType != MovieListType.SEARCH) {
			View listViewFooter = getLayoutInflater(savedInstanceState).inflate(R.layout.loading_footer, null);
			listView.addFooterView(listViewFooter, null, false);
		}

		/* set-up the movie adapter */
		if (movieAdapter == null) {
			movieAdapter = new MovieAdapter(getActivity(), R.layout.list_row, new ArrayList<Movie>());
		}
		setListAdapter(movieAdapter);
		listView.setOnScrollListener(this);

		MovieDBApplication app = (MovieDBApplication) getActivity().getApplication();
		dataMananger = app.getDataManager();
		dataMananger.getMovieListFragmentMap().put(fragmentType, new IMovieListFragmentCallback() {

			@Override
			/* show progress text and progress icon on the action bar */
			public void onGettingMovieList() {
				progressBar.setVisibility(View.VISIBLE);
			}

			@Override
			/* add movies to the Adapter */
			public void onMovieListRetrieved(final List<Movie> movies) {
				if (DEBUG) {
					Log.i(TAG, "inside onMovieListRetrieved(final List<Movie> movies)");
					Log.i(TAG, movies.toString());
				}
				movieAdapter.clear();
				for (Movie movie : movies) {
					movieAdapter.add(movie);
				}
				/* add all the movies + the footer */
				adapterEmpty = false;
				adapterChildCount = movieAdapter.getCount();
				movieAdapter.notifyDataSetChanged();
				if (DEBUG) {
					Log.e(TAG, "mMovieAdapter.getCount() after resetting movies = " + adapterChildCount);
				}
				/* now download posters */
				getPostersForVisibleMovies();
				progressBar.setVisibility(View.INVISIBLE);
			}

			@Override
			public void onMovieListUpdated(final List<Movie> movies) {
				if (DEBUG) {
					Log.i(TAG, "inside onMovieListUpdated(final List<Movie> movies)");
					Log.i(TAG, movies.toString());
				}
				wentFetching = false;
				for (Movie movie : movies) {
					movieAdapter.add(movie);
				}
				/* add all the movies + the footer */
				movieAdapter.notifyDataSetChanged();
				adapterChildCount = movieAdapter.getCount();

				/* now download posters if it's the first pass */
				getPostersForVisibleMovies();

				progressBar.setVisibility(View.INVISIBLE);
				if (DEBUG) {
					Log.e(TAG, "mMovieAdapter.getCount() after adding movies = " + adapterChildCount);
				}
			}

			@Override
			public void onMoviePosterRetreived(final boolean done) {
				if (DEBUG) {
					Log.i(TAG, "inside onMoviePosterRetreived()");
				}
				movieAdapter.notifyDataSetChanged();
				if (done) {
					listReady = true;
				}
			}

			@Override
			public void onMovieListRetrievalFailed() {
				AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
				/* if getting movie list failed re-try or exit */
				builder.setMessage(R.string.want_retry).setCancelable(false)
						.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
							@Override
							public void onClick(final DialogInterface dialog, final int id) {
								dataMananger.provideMovieList(fragmentType, searchQuery);
							}
						}).setNegativeButton("No", new DialogInterface.OnClickListener() {
							@Override
							public void onClick(final DialogInterface dialog, final int id) {
								getActivity().finish();
							}
						});
				AlertDialog alert = builder.create();
				alert.show();
			}
		});
		/* data manager is set up, so you can call it from MovieListHostFragment */

		if (startupList) {
			/* get movies from cache or download them if needed */
			if (DEBUG) {
				Log.e(TAG, "going to get movies for the startup page");
			}
			dataMananger.provideMovieList(fragmentType, searchQuery);
		}
	}

	@Override
	public void onResume() {
		super.onResume();
		if (DEBUG) {
			Log.i(TAG, "onResume() and fragmentType is " + fragmentType);
		}
	}

	@Override
	public void onSaveInstanceState(final Bundle outState) {
		if (searchQuery != null) {
			Log.d(TAG, "in onSaveInstanceState() mSearchQuery = " + searchQuery + " fragmentType = " + fragmentType);
			outState.putString("search_query", searchQuery);
		}
	}

	@Override
	public void onPause() {
		super.onPause();
		Log.d(TAG, "onPause() for fragmentType " + fragmentType);
		dataMananger.cancelMovieListFetching();
		dataMananger.cancelPosterDownloading();
	}

	@Override
	public void onDestroyView() {
		super.onDestroyView();
		if (DEBUG) {
			Log.i(TAG, "onDestroyView() and fragmentType is " + fragmentType);
		}
	}

	/* on list item click trigger the listener and pass it the movie */
	@Override
	public void onListItemClick(final ListView l, final View v, final int position, final long id) {
		if (listReady) {
			Movie movie = movieAdapter.getItem(position);
			/* if user tries to click on a null search result do nothing */
			if (!movie.getName().equals("Nothing found.")) {
				onMovieSelectedListener.onMovieSelected(movie);
			}
		}
	}

	public void setSearchQuery(final String mSearchQuery) {
		this.searchQuery = mSearchQuery;
	}

	public boolean isAdapterEmpty() {
		return adapterEmpty;
	}

	public void setStartupList(final boolean startupList) {
		this.startupList = startupList;
	}

	public DataManager getDataMananger() {
		return dataMananger;
	}

	@Override
	public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
			final int totalItemCount) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onScrollStateChanged(final AbsListView view, final int scrollState) {
		if (DEBUG) {
			Log.d(TAG, "onScrollStateChanged");
		}
		switch (scrollState) {
			case SCROLL_STATE_IDLE:
				Log.e(TAG, "SCROLL_STATE_IDLE");
				getPostersForVisibleMovies();
				movieAdapter.setBusy(false);
				movieAdapter.notifyDataSetChanged();
				if (isEndOfList()) {
					if (DEBUG) {
						Log.e(TAG, "going to get movies for the next page");
					}
					dataMananger.provideNextPage(fragmentType);
				}
				break;
			case SCROLL_STATE_TOUCH_SCROLL:
				Log.d(TAG, "SCROLL_STATE_TOUCH_SCROLL");
				listReady = false;
				/* if there's already a task running to get posters - cancel */
				dataMananger.cancelPosterDownloading();
				movieAdapter.setBusy(true);
				break;
			case SCROLL_STATE_FLING:
				Log.d(TAG, "SCROLL_STATE_FLING");
				listReady = false;
				/* if there's already a task running to get posters - cancel */
				dataMananger.cancelPosterDownloading();
				movieAdapter.setBusy(true);
				break;
			default:
				Log.d(TAG, "-=UNKNOWN=-");
				break;
		}
	}

	public void notifyAdapter(final boolean go) {
		if (DEBUG) {
			Log.d(TAG, "notifyAdapter  go? " + go + " fragment " + fragmentType);
		}
		if (go) {
			getPostersForVisibleMovies();
		}
		movieAdapter.setBusy(!go);
		movieAdapter.notifyDataSetChanged();
	}

	private void getPostersForVisibleMovies() {
		if (DEBUG) {
			Log.d(TAG, "getPostersForVisibleMovies()");
		}
		/* if adapterChildCount == 0 do nothing */
		if (adapterChildCount == 0) {
			return;
		}

		int firstPos = listView.getFirstVisiblePosition();
		int lastPos = listView.getLastVisiblePosition();

		if (fragmentType == MovieListType.SEARCH) {
			if (lastPos == -1) {
				lastPos = DEFAULT_VISIBLE_ROWS - 1;
				while (lastPos < adapterChildCount) {
					lastPos--;
				}
				/* listView.getLastVisiblePosition() returns 0 for some reason so just get 4 posters */
				List<Movie> movies = new ArrayList<Movie>();
				selectMovies(firstPos, lastPos, movies);
				dataMananger.provideMoviePosters(movies, fragmentType);
			} else {
				List<Movie> movies = new ArrayList<Movie>();
				selectMovies(firstPos, lastPos, movies);
				dataMananger.provideMoviePosters(movies, fragmentType);
			}
			return;
		}

		/* don't include listview footer */
		if (lastPos == adapterChildCount) {
			lastPos--;
		}

		int firstPosOnNewPage = adapterChildCount - DataManager.DEFAULT_LIST_SIZE;
		if (DEBUG) {
			Log.d(TAG, "pos = " + firstPos + " lastPos = " + lastPos + " firstPosOnNewPage " + firstPosOnNewPage);
		}
		if (lastPos == 0 || lastPos == firstPosOnNewPage) {
			if ((lastPos = DEFAULT_VISIBLE_ROWS - 1) < adapterChildCount) {
				/* listView.getLastVisiblePosition() returns 0 for some reason so just get 4 posters */
				List<Movie> movies = new ArrayList<Movie>();
				selectMovies(firstPos, lastPos, movies);
				dataMananger.provideMoviePosters(movies, fragmentType);
			}
		} else if (lastPos == firstPosOnNewPage) {
			if ((lastPos = firstPosOnNewPage + DEFAULT_VISIBLE_ROWS - 1) < adapterChildCount) {
				/* listView.getLastVisiblePosition() returns 0 for some reason so just get 4 posters */
				List<Movie> movies = new ArrayList<Movie>();
				selectMovies(firstPos, lastPos, movies);
				dataMananger.provideMoviePosters(movies, fragmentType);
			}
		} else {
			List<Movie> movies = new ArrayList<Movie>();
			selectMovies(firstPos, lastPos, movies);
			dataMananger.provideMoviePosters(movies, fragmentType);
		}
	}

	private void selectMovies(int pos, final int lastPos, final List<Movie> movies) {
		if (DEBUG) {
			Log.d(TAG, "inside selectMovies pos = " + pos + " lastPos = " + lastPos);
		}
		movies.clear();
		while (pos <= lastPos) {
			Movie movie = movieAdapter.getItem(pos);
			/* only include those posters that are recycled */
			if (movie.isPosterRecycled()) {
				movies.add(movie);
				if (DEBUG) {
					Log.e(TAG, "selectMovies movie " + movie.getName() + " poster for position " + pos);
				}
			}
			pos++;
		}
	}

	private boolean isEndOfList() {
		/* don't trigger the list fetching if already went for the list */
		if (wentFetching) {
			return false;
		}
		if (fragmentType == MovieListType.SEARCH) {
			return false;
		}
		int lastPos = listView.getLastVisiblePosition();
		if (DEBUG) {
			Log.d(TAG, "lastPos " + lastPos + " adapterChildCount " + adapterChildCount);
		}
		if (lastPos == adapterChildCount) {
			wentFetching = true;
			return true;
		}
		return false;
	}
}
